package net.gdface.bean.descriptor;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import gu.sql2java.BaseRow;
import gu.sql2java.RowMetaData;
import net.gdface.reflection.MethodUtils;

import static com.google.common.base.Preconditions.checkNotNull;
import static net.gdface.utils.CaseSupport.toCamelcase;

/**
 * 实现对{@link gu.sql2java.BaseBean}接口实例的属性封装
 * @author guyadong
 * @since 2.7.0
 */
public class BaseRowPropertyDescriptor extends PropertyDescriptor {
    /**
     * The parameter types array for the reader method signature.
     */
    private static final Class<?>[] EMPTY_PARAMETER = new Class[]{};
	private final Method readMethod;
	private final Method writeMethod;
	private final Class<?> propertyType;
	public BaseRowPropertyDescriptor(String name,BaseRow bean)
			throws IntrospectionException {
		super(checkNotNull(name,"field is null"), null, null);
		Class<?> beanClass = checkNotNull(bean,"bean is null").getClass();
        Class<?> fieldType = typeOf(checkNotNull(beanClass,"beanClass is null"),name);
        RowMetaData metaData = bean.fetchMetaData();
        Class<?> columnType = metaData.columnTypeOf(name);
        /** 通过两将转换，计算出数据库字段名 */
        String columnName = metaData.columnNameOf(metaData.columnIDOf(name));
        if(fieldType.equals(columnType)){
        	/** 字段定义与metaData定义的类型相同时，可以直接调用标准的getter/setter方法 */
        	Method method;
        	// Look for mapped read method and matching write method
        	try {
        		method = getMethod(beanClass, toCamelcase("get_" + columnName), EMPTY_PARAMETER);
            } catch (IntrospectionException e) {
            	method = getMethod(beanClass, toCamelcase("is_" + columnName), EMPTY_PARAMETER);
            }
        	readMethod = method;
        	writeMethod = getMethod(beanClass, toCamelcase("set_" + columnName), new Class<?>[]{columnType});
        	propertyType = columnType;
        }else{
        	/** 字段定义与metaData定义的类型不相同时，调用的read/write方法 */
        	// Look for mapped read method and matching write method
        	readMethod = getMethod(beanClass, toCamelcase("read_" + columnName), EMPTY_PARAMETER);
        	writeMethod = getMethod(beanClass, toCamelcase("write_" + columnName), new Class<?>[]{fieldType});
        	propertyType = fieldType;        	
        }
	}
	private static Class<?> typeOf(Class<?> beanClass, String name){
		Field field = DescriptorUtils.fieldOf(beanClass,name);
		return null== field ? null : field.getType();
	}
	@Override
	public Method getReadMethod() {
		return readMethod;
	}

	@Override
	public Method getWriteMethod() {
		return writeMethod;
	}

	@Override
	public Class<?> getPropertyType() {
		return propertyType;
	}
    /**
     * Find a method on a class with a specified parameter list.
     */
    private static Method getMethod(Class<?> clazz, String methodName, Class<?>[] parameterTypes)
                                           throws IntrospectionException {
        if (methodName == null) {
            return null;
        }

        Method method = MethodUtils.getMatchingAccessibleMethod(clazz, methodName, parameterTypes);
        if (method != null) {
            return method;
        }

        int parameterCount = (parameterTypes == null) ? 0 : parameterTypes.length;

        // No Method found
        throw new IntrospectionException("No method \"" + methodName +
                "\" with " + parameterCount + " parameter(s) of matching types.");
    }
}
